﻿using System;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace YmtAuth.AppService
{
    internal class TokenCache<TKey, TValue>
    {
        private static int limitCount = 0;
        private static readonly ConcurrentDictionary<TKey, CacheItem> cache = new ConcurrentDictionary<TKey, CacheItem>();

        internal static void TryAdd(TKey key, TValue item)
        {
            if (!EnableTokenCache)
                return;
            RemoveExpiredCacheItem();
            AddCacheItem(key, item);
        }

        internal static bool Exists(TKey key)
        {
            if (!EnableTokenCache) return false;
            return cache.ContainsKey(key);
        }
        internal static TValue GetItem(TKey key)
        {
            CacheItem item = new CacheItem();
            if (!EnableTokenCache) return item.Value;

            if (cache.TryGetValue(key, out item))
            {
                if (item.ExpiredTime > DateTime.Now)
                    return item.Value;
                else
                    return default(TValue);
            }
            return item.Value;
        }
        internal static void RemoveItem(TKey key)
        {
            if (!EnableTokenCache) return;
            CacheItem item;
            cache.TryRemove(key, out item);
        }

        internal static void RemoveItemByValue(TValue value)
        {
            if (!EnableTokenCache) return;
            var keys = cache.Where(e => e.Value.Value.Equals(value)).AsParallel().Select(e => e.Key);
            if (keys.Any())
            {
                //foreach (var k in keys)
                Parallel.ForEach(keys, k =>
                    RemoveItem(k));
            }
        }

        private static void AddCacheItem(TKey key, TValue item)
        {
            try
            {
                cache.TryAdd(key, new CacheItem { Value = item, ExpiredTime = DateTime.Now.Add(ExpiredTimeSpan) });
            }
            catch (Exception ex)
            {
                YmtSystem.CrossCutting.YmatouLoggingService.Error("token 缓存异常 {0}", ex);
            }
        }


        private static void RemoveExpiredCacheItem()
        {
            if (cache.Count >= LimitCount)
            {
                var keys = cache.Where(e => e.Value.ExpiredTime <= DateTime.Now).AsParallel().Select(e => e.Key);
                if (keys.Any())
                {
                    //foreach (var k in keys)
                    Parallel.ForEach(keys, k =>
                        RemoveItem(k));
                }
                else
                {
                    cache.Clear();
                }
            }
        }

        private static int LimitCount { get { return limitCount <= 0 ? Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["TokenCacheLimit"]) : limitCount; } }
        //配置不为空，则启用tokencache功能
        private static bool EnableTokenCache { get { return !string.IsNullOrEmpty(System.Configuration.ConfigurationManager.AppSettings["TokenCacheTimeOut"]); } }
        private static TimeSpan ExpiredTimeSpan
        {
            get
            {
                var exTimeSpan = System.Configuration.ConfigurationManager.AppSettings["TokenCacheTimeOut"];
                if (string.IsNullOrEmpty(exTimeSpan)) return TimeSpan.FromMinutes(3);
                return TimeSpan.Parse(exTimeSpan);
            }
        }

        internal struct CacheItem
        {
            public TValue Value { get; set; }
            public DateTime ExpiredTime { get; set; }
        }
    }
}
